const { exec, execSync, spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
const AdmZip = require('adm-zip');

/**
 * 解压ZIP文件
 * @param {Object} config - 解压配置
 * @param {string} config.origin - ZIP文件路径
 * @param {string} [config.dist] - 解压输出目录，默认与ZIP文件在同一目录
 * @param {boolean} [config.createFolder=null] - 是否创建额外的文件夹 (null表示自动判断)
 * @param {boolean} [config.overwrite=true] - 是否覆盖同名文件
 * @param {Object} [config.fileMode] - 解压文件的权限
 * @param {number} [config.fileMode.file=0o777] - 文件权限 (默认: 0o777 = rwxrwxrwx)
 * @param {number} [config.fileMode.dir=0o777] - 目录权限 (默认: 0o777 = rwxrwxrwx)
 * @param {number} [config.largeFileSizeThreshold=100] - 大文件阈值(MB)，超过此值使用子进程解压
 * @returns {Promise<{outputDir: string}>} - 返回实际的输出目录
 */
async function unzipFile(config) {
    // 设置默认选项
    const defaultConfig = {
        origin: '',
        dist: '',
        createFolder: null, // null表示自动判断
        overwrite: true,
        fileMode: {
            file: 0o777, // rwxrwxrwx
            dir: 0o777    // rwxrwxrwx
        },
        largeFileSizeThreshold: 100 // 100MB
    };
    
    // 合并选项
    const cfg = { ...defaultConfig, ...config };
    if (config.fileMode) {
        cfg.fileMode = { ...defaultConfig.fileMode, ...config.fileMode };
    }
    
    // 验证必要参数
    if (!cfg.origin) {
        throw new Error('必须提供ZIP文件路径 (origin)');
    }
    
    // 如果未提供输出目录，默认使用ZIP文件所在目录
    if (!cfg.dist) {
        cfg.dist = path.dirname(cfg.origin);
    }
    
    // 确保输出目录存在
    if (!fs.existsSync(cfg.dist)) {
        fs.mkdirSync(cfg.dist, { recursive: true });
        // 设置目录权限
        if (process.platform !== 'win32') {
            fs.chmodSync(cfg.dist, cfg.fileMode.dir);
        }
    }

    console.log(`开始解压文件: ${cfg.origin} 到 ${cfg.dist}`);
    
    // 获取ZIP文件名（不含扩展名）
    const zipFileName = path.basename(cfg.origin, path.extname(cfg.origin));
    
    // 获取文件大小（MB）
    const fileSizeMB = fs.statSync(cfg.origin).size / (1024 * 1024);
    console.log(`ZIP文件大小: ${fileSizeMB.toFixed(2)}MB`);
    
    // 如果createFolder为null，则自动判断是否需要创建文件夹
    if (cfg.createFolder === null) {
        cfg.createFolder = await shouldCreateFolder(cfg.origin, zipFileName);
    }
    
    console.log(`选项: 创建文件夹=${cfg.createFolder}, 覆盖文件=${cfg.overwrite}, 文件权限=${cfg.fileMode.file.toString(8)}, 目录权限=${cfg.fileMode.dir.toString(8)}`);
    
    // 如果需要创建额外的文件夹，则创建一个以ZIP文件名命名的子目录
    let actualOutputDir = cfg.dist;
    if (cfg.createFolder) {
        actualOutputDir = path.join(cfg.dist, zipFileName);
        
        if (!fs.existsSync(actualOutputDir)) {
            fs.mkdirSync(actualOutputDir, { recursive: true });
            // 设置目录权限
            if (process.platform !== 'win32') {
                fs.chmodSync(actualOutputDir, cfg.fileMode.dir);
            }
        }
        console.log(`创建文件夹: ${actualOutputDir}`);
    }
    
    // 根据文件大小选择解压方法
    if (fileSizeMB > cfg.largeFileSizeThreshold) {
        console.log(`文件大小超过${cfg.largeFileSizeThreshold}MB，使用子进程解压...`);
        try {
            await unzipWithChildProcess(cfg.origin, actualOutputDir, cfg);
        } catch (err) {
            console.log(`子进程解压失败: ${err.message}`);
            console.log('尝试使用系统命令解压...');
            await unzipWithSystemCommand(cfg.origin, actualOutputDir, cfg);
        }
    } else {
        // 尝试使用系统命令解压
        try {
            await unzipWithSystemCommand(cfg.origin, actualOutputDir, cfg);
        } catch (err) {
            console.log(`系统命令解压失败: ${err.message}`);
            console.log('尝试使用 AdmZip 解压...');
            
            // 如果系统命令失败，使用 AdmZip 作为备选方案
            await unzipWithAdmZip(cfg.origin, actualOutputDir, cfg);
        }
    }
    
    // 创建"新建文件夹"目录
    const newFolderPath = path.join(actualOutputDir, '新建文件夹');
    if (!fs.existsSync(newFolderPath)) {
        fs.mkdirSync(newFolderPath, { recursive: true });
        console.log(`创建特殊目录: ${newFolderPath}`);
        
        // 设置目录权限
        if (process.platform !== 'win32') {
            fs.chmodSync(newFolderPath, cfg.fileMode.dir);
        }
    }
    
    console.log('解压完成');
    
    // 返回实际的输出目录
    return { outputDir: actualOutputDir };
}

/**
 * 判断是否需要创建额外的文件夹
 * @param {string} zipFilePath - ZIP文件路径
 * @param {string} zipFileName - ZIP文件名（不含扩展名）
 * @returns {Promise<boolean>} - 是否需要创建文件夹
 */
async function shouldCreateFolder(zipFilePath, zipFileName) {
    try {
        const zip = new AdmZip(zipFilePath);
        const entries = zip.getEntries();
        
        // 检查是否所有条目都在同一个根文件夹下，且该根文件夹与ZIP文件同名
        if (entries.length === 0) {
            // 空ZIP文件，创建文件夹
            return true;
        }
        
        // 检查是否有单一的根文件夹
        const rootFolders = new Set();
        let hasRootFiles = false;
        
        for (const entry of entries) {
            const entryPath = entry.entryName;
            const parts = entryPath.split('/');
            
            if (parts.length === 1 && !entry.isDirectory) {
                // 根目录下有文件
                hasRootFiles = true;
                break;
            } else if (parts.length > 1) {
                // 添加第一级目录
                rootFolders.add(parts[0]);
            }
        }
        
        // 如果根目录下有文件，或者有多个根文件夹，则需要创建文件夹
        if (hasRootFiles || rootFolders.size !== 1) {
            return true;
        }
        
        // 检查唯一的根文件夹是否与ZIP文件同名
        const rootFolder = Array.from(rootFolders)[0];
        return rootFolder !== zipFileName;
        
    } catch (err) {
        console.error(`检查ZIP内容出错: ${err.message}`);
        // 出错时默认创建文件夹
        return true;
    }
}

/**
 * 使用系统命令解压
 * @param {string} zipFilePath - ZIP文件路径
 * @param {string} outputDir - 解压输出目录
 * @param {Object} options - 解压选项
 * @returns {Promise<void>}
 */
async function unzipWithSystemCommand(zipFilePath, outputDir, options) {
    return new Promise((resolve, reject) => {
        let command;
        
        if (process.platform === 'win32') {
            // 尝试检测是否有 PowerShell
            try {
                execSync('powershell -Command "exit"');
                // PowerShell 可用
                const forceFlag = options.overwrite ? '-Force' : '';
                command = `powershell -command "Expand-Archive -Path '${zipFilePath}' -DestinationPath '${outputDir}' ${forceFlag}"`;
            } catch (e) {
                // PowerShell 不可用，尝试使用 cmd 的 tar 命令 (Windows 10 1803+ 支持)
                try {
                    execSync('tar --version');
                    command = `tar -xf "${zipFilePath}" -C "${outputDir}"`;
                } catch (e2) {
                    // tar 也不可用，尝试使用 VBScript 解压 (适用于旧版 Windows)
                    const vbsPath = path.join(__dirname, 'unzip.vbs');
                    fs.writeFileSync(vbsPath, getVBScriptContent(zipFilePath, outputDir, options));
                    command = `cscript //NoLogo "${vbsPath}"`;
                }
            }
        } else {
            // Linux/Mac 使用 unzip 命令
            const overwriteFlag = options.overwrite ? '-o' : '-n';
            command = `unzip ${overwriteFlag} "${zipFilePath}" -d "${outputDir}"`;
        }
        
        console.log(`执行命令: ${command}`);
        
        exec(command, (error, stdout, stderr) => {
            if (error) {
                console.error(`执行命令出错: ${error.message}`);
                reject(error);
                return;
            }
            
            if (stderr && stderr.trim() !== '') {
                console.error(`命令错误输出: ${stderr}`);
            }
            
            if (stdout && stdout.trim() !== '') {
                console.log(`命令输出: ${stdout}`);
            }
            
            // 如果使用了临时 VBS 脚本，删除它
            if (command.includes('unzip.vbs') && fs.existsSync(path.join(__dirname, 'unzip.vbs'))) {
                fs.unlinkSync(path.join(__dirname, 'unzip.vbs'));
            }
            
            // 在非Windows系统上设置文件权限
            if (process.platform !== 'win32') {
                setFilePermissions(outputDir, options.fileMode);
            }
            
            resolve();
        });
    });
}

/**
 * 使用 AdmZip 解压
 * @param {string} zipFilePath - ZIP文件路径
 * @param {string} outputDir - 解压输出目录
 * @param {Object} options - 解压选项
 * @returns {Promise<void>}
 */
async function unzipWithAdmZip(zipFilePath, outputDir, options) {
    return new Promise((resolve, reject) => {
        try {
            const zip = new AdmZip(zipFilePath);
            
            // 输出所有条目以便调试
            console.log('ZIP文件中的条目:');
            const entries = zip.getEntries();
            entries.forEach(entry => {
                console.log(`- ${entry.entryName} (${entry.isDirectory ? '目录' : '文件'})`);
            });
            
            // 如果不覆盖文件，需要手动处理
            if (!options.overwrite) {
                for (const entry of entries) {
                    const targetPath = path.join(outputDir, entry.entryName);
                    
                    // 如果文件已存在且不覆盖，则跳过
                    if (!entry.isDirectory && fs.existsSync(targetPath)) {
                        console.log(`跳过已存在的文件: ${targetPath}`);
                        continue;
                    }
                    
                    // 如果是目录
                    if (entry.isDirectory) {
                        if (!fs.existsSync(targetPath)) {
                            fs.mkdirSync(targetPath, { recursive: true });
                            console.log(`创建目录: ${targetPath}`);
                            
                            // 设置目录权限
                            if (process.platform !== 'win32') {
                                fs.chmodSync(targetPath, options.fileMode.dir);
                            }
                        }
                    } else {
                        // 如果是文件，确保其所在目录存在
                        const dirName = path.dirname(targetPath);
                        if (!fs.existsSync(dirName)) {
                            fs.mkdirSync(dirName, { recursive: true });
                            
                            // 设置目录权限
                            if (process.platform !== 'win32') {
                                fs.chmodSync(dirName, options.fileMode.dir);
                            }
                        }
                        
                        // 提取文件
                        const content = entry.getData();
                        fs.writeFileSync(targetPath, content);
                        console.log(`解压文件: ${targetPath}`);
                        
                        // 设置文件权限
                        if (process.platform !== 'win32') {
                            fs.chmodSync(targetPath, options.fileMode.file);
                        }
                    }
                }
            } else {
                // 直接解压所有文件
                zip.extractAllTo(outputDir, true);
                
                // 设置文件权限
                if (process.platform !== 'win32') {
                    setFilePermissions(outputDir, options.fileMode);
                }
            }
            
            resolve();
        } catch (err) {
            reject(err);
        }
    });
}

/**
 * 递归设置目录中所有文件和文件夹的权限
 * @param {string} dir - 目录路径
 * @param {Object} fileMode - 文件权限
 */
function setFilePermissions(dir, fileMode) {
    if (process.platform === 'win32') return; // Windows不支持chmod
    
    const files = fs.readdirSync(dir);
    
    for (const file of files) {
        const filePath = path.join(dir, file);
        const stat = fs.statSync(filePath);
        
        if (stat.isDirectory()) {
            // 设置目录权限
            fs.chmodSync(filePath, fileMode.dir);
            // 递归处理子目录
            setFilePermissions(filePath, fileMode);
        } else {
            // 设置文件权限
            fs.chmodSync(filePath, fileMode.file);
        }
    }
}

/**
 * 获取用于解压的 VBScript 内容
 * @param {string} zipFilePath - ZIP文件路径
 * @param {string} outputDir - 解压输出目录
 * @param {Object} options - 解压选项
 * @returns {string} VBScript 内容
 */
function getVBScriptContent(zipFilePath, outputDir, options) {
    const overwriteFlag = options.overwrite ? 'True' : 'False';
    
    return `
    ' VBScript to extract ZIP files
    ' For older Windows systems without PowerShell or tar
    
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set shell = CreateObject("Shell.Application")
    
    ' Create output directory if it doesn't exist
    If Not fso.FolderExists("${outputDir.replace(/\\/g, '\\\\')}") Then
        fso.CreateFolder("${outputDir.replace(/\\/g, '\\\\')}")
    End If
    
    ' Get absolute paths
    zipFile = fso.GetAbsolutePathName("${zipFilePath.replace(/\\/g, '\\\\')}")
    extractTo = fso.GetAbsolutePathName("${outputDir.replace(/\\/g, '\\\\')}")
    
    ' Extract the zip file
    Set zip = shell.NameSpace(zipFile)
    Set destination = shell.NameSpace(extractTo)
    
    If Not zip Is Nothing And Not destination Is Nothing Then
        ' 4 = Do not display a progress dialog box
        ' 16 = Respond with "Yes to All" for any dialog box that is displayed
        ' Only use 16 if overwrite is true
        Dim flags
        If ${overwriteFlag} Then
            flags = 4 + 16
        Else
            flags = 4
        End If
        
        destination.CopyHere zip.Items, flags
        WScript.Echo "Extraction completed successfully."
    Else
        WScript.Echo "Error: Could not access the zip file or destination folder."
    End If
    `;
}

/**
 * 使用子进程解压大文件
 * @param {string} zipFilePath - ZIP文件路径
 * @param {string} outputDir - 解压输出目录
 * @param {Object} options - 解压选项
 * @returns {Promise<void>}
 */
async function unzipWithChildProcess(zipFilePath, outputDir, options) {
    return new Promise((resolve, reject) => {
        let command, args;
        
        if (process.platform === 'win32') {
            // 在Windows上使用PowerShell
            try {
                execSync('powershell -Command "exit"');
                // PowerShell可用
                const forceFlag = options.overwrite ? '-Force' : '';
                command = 'powershell';
                args = [
                    '-command',
                    `Expand-Archive -Path '${zipFilePath}' -DestinationPath '${outputDir}' ${forceFlag}`
                ];
            } catch (e) {
                // PowerShell不可用，尝试使用tar
                try {
                    execSync('tar --version');
                    command = 'tar';
                    args = ['-xf', zipFilePath, '-C', outputDir];
                } catch (e2) {
                    // 如果tar也不可用，则失败
                    reject(new Error('无法找到合适的解压工具'));
                    return;
                }
            }
        } else {
            // Linux/Mac使用unzip
            const overwriteFlag = options.overwrite ? '-o' : '-n';
            command = 'unzip';
            args = [overwriteFlag, zipFilePath, '-d', outputDir];
        }
        
        console.log(`执行子进程命令: ${command} ${args.join(' ')}`);
        
        const childProcess = spawn(command, args, {
            stdio: ['ignore', 'pipe', 'pipe']
        });
        
        let stdout = '';
        let stderr = '';
        
        childProcess.stdout.on('data', (data) => {
            const chunk = data.toString();
            stdout += chunk;
            // 实时输出进度信息
            if (chunk.trim()) {
                console.log(`解压进度: ${chunk.trim()}`);
            }
        });
        
        childProcess.stderr.on('data', (data) => {
            stderr += data.toString();
        });
        
        childProcess.on('close', (code) => {
            if (code !== 0) {
                console.error(`子进程退出，代码: ${code}`);
                console.error(`错误输出: ${stderr}`);
                reject(new Error(`子进程解压失败，退出代码: ${code}`));
                return;
            }
            
            if (stdout.trim()) {
                console.log(`子进程输出: ${stdout.trim()}`);
            }
            
            // 在非Windows系统上设置文件权限
            if (process.platform !== 'win32') {
                setFilePermissions(outputDir, options.fileMode);
            }
            
            resolve();
        });
        
        childProcess.on('error', (err) => {
            console.error(`子进程错误: ${err.message}`);
            reject(err);
        });
    });
}

// 如果直接运行此文件，则执行简单示例
if (require.main === module) {
    // 简单的命令行解析
    const args = process.argv.slice(2);
    if (args.length === 0) {
        console.log('用法: node unzip.js <zip文件路径> [输出目录]');
        process.exit(1);
    }
    
    const config = {
        origin: args[0],
        dist: args.length > 1 ? args[1] : ''
    };
    
    unzipFile(config)
        .then(result => {
            console.log(`文件已解压到: ${result.outputDir}`);
        })
        .catch(err => {
            console.error('解压失败:', err);
            process.exit(1);
        });
}

// 导出函数供其他模块使用
module.exports = {
    unzipFile,
    shouldCreateFolder,
    unzipWithSystemCommand,
    unzipWithAdmZip,
    unzipWithChildProcess,
    setFilePermissions,
    getVBScriptContent
};